﻿<#
.SYNOPSIS  
    Installs and deploys solutions specified in Upgrade.xml.  Solutions should be placed in the Solutions folder. 
.DESCRIPTION  
    In some scenarios, solutions may fail to install.  In this case, this script will time out and move to the next solution. Be sure to review the upgrade logs for deployment errors.
.NOTES
    If the solution is already installed, it will be skipped unless RemoveIfInstalled is set to TRUE.  In this case, the solution will be uninstalled first and then reinstalled. 
.LINK  
    http://7up.codeplex.com 
#>

Write-Host ("   Adding solutions ({0})..." -f [DateTime]::Now.ToString())

#add SharePoint snap in
if ((Get-PSSnapin -Name  Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue) -eq $null) 
{ 
    Add-PsSnapin  Microsoft.SharePoint.PowerShell 
}

<#
 Runs pending timer jobs
#>
function Execute-AdminServiceJobs()
{
	Stop-Service -Name "SPAdminV4" -Confirm:$false
	Start-SPAdminJob -Confirm:$false
	Start-Service -Name "SPAdminV4" -Confirm:$false
}

<#
 Gets a string value as a bool
#>
function Get-BoolValue([string]$stringValue, $defaultValue = $true)
{
	if([string]::IsNullOrEmpty($stringValue) -eq $false) 
	{
		return [bool]::Parse($stringValue)
	}
	return $defaultValue
}

<#
 Uninstalls the specified solution name from the specified applications
#>
function Uninstall-Solution([string]$name, [string[]]$webApps = @())
{
    $solution = Get-SPSolution $name -ErrorAction SilentlyContinue
    
	# if solution installed
    if ($solution -ne $null) 
	{
		# if solution deployed, retract
        if ($solution.Deployed) 
		{
			# if deployed to web applications, retract from each
            if ($solution.ContainsWebApplicationResource) 
			{
			    if ($webApps -eq $null -or $webApps.Length -eq 0) 
				{
					Write-Warning "      The solution $name contains web application resources but no web applications were specified to deploy to."
				}
				else 
				{
					foreach($webApp in $webApps)
					{
						Write-Host "      Retracting solution $name from $webApp..."
                		$solution | Uninstall-SPSolution -WebApplication $webApp -Confirm:$false
						
						Execute-AdminServiceJobs
					}
				}
            } 
			else # globally deployed, retract
			{
				Write-Host "      Retracting solution $name..."
                $solution | Uninstall-SPSolution -Confirm:$false
				Execute-AdminServiceJobs
            }
        
            # block until the solution is no longer deployed
            do { Start-Sleep 2 } while ((Get-SPSolution $name).Deployed)
        }
        
        # remove the solution
        Write-Host "      Remvoing solution $name..."
        Get-SPSolution $name | Remove-SPSolution -Confirm:$false
    }
}

<#
 Installs the specified solution file
#>
function Install-Solution([string]$path, [bool]$gac, [bool]$cas, [string[]]$webApps = @(), [bool]$force)
{
    [string]$name = Split-Path -Path $path -Leaf
    
	$solution = Get-SPSolution $name -ErrorAction SilentlyContinue
    
	# if solution installed
    if ($solution -ne $null) 
	{
		Write-Host "      Solution $name already added.."
		if($solution.Deployed)
		{
			Write-Host "      Solution $name already deployed.."
			return
		}
	}
	else
	{
    	# add solution
    	Write-Host "      Adding solution $name..."
    	$solution = Add-SPSolution $path
    }
	
    # deploy to farm
    if (!$solution.ContainsWebApplicationResource) 
	{
		$solRetryCount = 0
        Write-Host "      Deploying solution $name to the farm..." -NoNewLine
        $solution | Install-SPSolution -GACDeployment:$gac -CASPolicies:$cas -Confirm:$false -Force:$force
		Execute-AdminServiceJobs
		
    	do 
    	{ 
    		Start-Sleep 2
    		$solRetryCount++
    		Write-Host "." -NoNewLine
    		if($solRetryCount -gt 60)
    		{
    			Write-Host ""
    			Write-Error "         Timeout deploying solution $name to the farm"
    			break
    		}
    	} while ((Get-SPSolution $name).JobExists)
		
    	Write-Host ""
		$solution = (Get-SPSolution $name)
		if($solution.LastOperationResult -ne "DeploymentSucceeded")
		{
			Write-Error $solution.LastOperationDetails
		}
    } 
	else # deploy to web applications
	{
        if ($webApps -eq $null -or $webApps.Length -eq 0) 
		{
            Write-Warning "      The solution $name contains web application resources but no web applications were specified to deploy to."
            return
        }
		foreach($webApp in $webApps)
		{
			$retryCount = 0
            Write-Host "      Deploying solution $name to $webApp..." -NoNewLine
			if((Get-SPWebApplication $webApp -ErrorAction SilentlyContinue) -ne $null)
			{
				$solution | Install-SPSolution -GACDeployment:$gac -CASPolicies:$cas -WebApplication $webApp -Confirm:$false -Force:$force
				Execute-AdminServiceJobs
				
				# block until deployed
				do 
				{ 
					Start-Sleep 2
					$retryCount++
					Write-Host "." -NoNewLine 
    				if($retryCount -gt 60)
    				{
    					Write-Host ""
    					Write-Error "         Timeout deploying solution $name to $webApp"
    					break
    				} 
				} while ((Get-SPSolution $name).JobExists)
				
				Write-Host ""
				$solution = (Get-SPSolution $name)
				if($solution.LastOperationResult -ne "DeploymentSucceeded")
				{
					Write-Error $solution.LastOperationDetails
				}
			}
			else
			{
				Write-Warning "      Skipping deployment to $webApp because the web application does not exist."
			}
        }
    }
}

# get config
$config = [xml](Get-Content Upgrade.xml)

# set base path
$solutionBasePath = ((Get-Location -PSProvider FileSystem).ProviderPath + "\Solutions\")

$removeIfInstalled = (Get-BoolValue $config.Settings.Solutions.RemoveIfInstalled $false)

# add each solution
foreach($solution in $config.Settings.Solutions.Solution)
{
	if($solution -ne $null)
	{
		$solutionPath = ($solutionBasePath + $solution.Name)
		$gacDeployment = (Get-BoolValue $solution.GACDeployment $false)
		$casPolicies = (Get-BoolValue $solution.CASPolicies $false)
		$force = (Get-BoolValue $solution.Force $false)

		if($removeIfInstalled -eq $true)
		{
			Uninstall-Solution $solution.Name $solution.WebApplications.WebApplication
		}
		
		# install
		Install-Solution $solutionPath $gacDeployment $casPolicies $solution.WebApplications.WebApplication $force
	}
}